package com.ecr.hub.service.data.input.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.richfaces.event.FileUploadEvent;
import org.richfaces.model.UploadedFile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;

import com.ecr.hub.ApplicationException;
import com.ecr.hub.DaoApplicationException;
import com.ecr.hub.DestinationUnknownException;
import com.ecr.hub.FileNotExcelFormatException;
import com.ecr.hub.database.dao.IDaoInputData;
import com.ecr.hub.model.HubInputData;
import com.ecr.hub.model.composition.Composition;
import com.ecr.hub.model.destination.DestinationRailTranscoder;
import com.ecr.hub.model.enumeration.HubDataInputStatus;
import com.ecr.hub.model.enumeration.HubSillonStatus;
import com.ecr.hub.model.enumeration.HubSillonType;
import com.ecr.hub.model.enumeration.HubTypeFile;
import com.ecr.hub.model.sillon.Sillon;
import com.ecr.hub.service.data.input.IInputDataManager;
import com.ecr.hub.service.data.input.xls.IXlsDecoder;
import com.ecr.hub.service.destination.IDestinationRailTranscoder;
import com.ecr.hub.service.hub.IHubManager;
import com.ecr.hub.util.MessageUtils;

@Service("inputDataManager")
public class InputDataManagerImpl implements IInputDataManager {

	// Le dao pour les ressources de type inputData.
	@Resource(name = "daoInputData")
	private IDaoInputData daoInputData;

	// La ressource pour le decodeur Xls.
	@Resource(name = "xlsDecoder")
	private IXlsDecoder xlsDecoder;

	// Ressource pour l'intégration des sillons et des compositions par IHM.
	@Resource(name = "hubManager")
	IHubManager hubManager;

	// Ressource pour la liste des voies / destinations.
	@Resource(name = "destinationRailTranscoder")
	IDestinationRailTranscoder destinationRailTranscoder;

	// Le répertoire de stockage des fichiers.
	@Value(value = "${repository.folder}")
	private String repositoryFolder;

	/**
	 * Mise à jour la table de pilotage pour la gestion des chargements de
	 * fichier (sillons et compositions).
	 */
	@Override
	public void update(HubInputData inputData) throws ApplicationException {

		try {
			daoInputData.update(inputData);
		} catch (DataAccessException e) {
			throw new DaoApplicationException(e);
		} catch (Exception e) {
			throw new ApplicationException(e);
		}
	}

	/**
	 * Récupération de toutes les lignes dans la table hub_input_data dont le
	 * statut est à "FILE_VALID" (dans notre cas mais la fonction est générique)
	 * afin de traiter la chargement des fichiers.
	 */
	@Override
	public List<HubInputData> findByStatus(HubDataInputStatus dataStatus)
			throws ApplicationException {

		try {
			return daoInputData.findByStatus(dataStatus);
		} catch (DataAccessException e) {
			throw new DaoApplicationException(e);
		} catch (Exception e) {
			throw new ApplicationException(e);
		}
	}

	/**
	 * Liste la totalité des enregistrements pour la table hub_input_data.
	 */
	@Override
	public List<HubInputData> getListAll() throws ApplicationException {

		try {
			List<HubInputData> lstInputDatas = daoInputData.getListAll();
			// Si vide laisse traiter par l'IHM.
			// On remplace le code par un libellé compréhensible par
			// l'utilisateur.
			for (HubInputData inputData : lstInputDatas) {
				getStatusLabel(inputData);
			}
			return lstInputDatas;
		} catch (DataAccessException e) {
			throw new DaoApplicationException(e);
		} catch (Exception e) {
			throw new ApplicationException(e);
		}
	}

	/**
	 * Récupération d'un libellé d'erreur compréhensible par l'utilisateur à
	 * partir des codes erreur récupérés du traitement automatique.
	 */
	private void getStatusLabel(HubInputData inputData)
			throws ApplicationException {
		String statusLabel = null;

		if (HubDataInputStatus.ERROR_MAIL_FILE_NOT_FOUND.equals(inputData
				.getStatus())) {
			statusLabel = MessageUtils
					.getStringMessageError("inputdata.mail.file.notfound");

		} else if (HubDataInputStatus.ERROR_MAIL_FILE_NOT_XLS.equals(inputData
				.getStatus())) {
			statusLabel = MessageUtils
					.getStringMessageError("inputdata.mail.file.notxls");

		} else if (HubDataInputStatus.ERROR_XLS_BAD_DESTINATION
				.equals(inputData.getStatus())) {
			statusLabel = MessageUtils
					.getStringMessageError("inputdata.xls.bad.destination");

		} else if (HubDataInputStatus.ERROR_XLS_BAD_FORMAT.equals(inputData
				.getStatus())) {
			statusLabel = MessageUtils
					.getStringMessageError("inputdata.xls.bad.format");

		} else if (HubDataInputStatus.ERROR_XLS_WRITING.equals(inputData
				.getStatus())) {
			statusLabel = MessageUtils
					.getStringMessageError("inputdata.xls.writing");

		} else if (HubDataInputStatus.ERROR_XLS_BAD_SILLON.equals(inputData
				.getStatus())) {
			statusLabel = MessageUtils
					.getStringMessageError("inputdata.xls.bad.sillon");

		} else if (HubDataInputStatus.ERROR_XLS_BAD_SILLON_PLAN
				.equals(inputData.getStatus())) {
			statusLabel = MessageUtils
					.getStringMessageError("inputdata.xls.bad.sillon.plan");

		} else if (HubDataInputStatus.ERROR_XLS_DAO_ACCESS_EXCEPTION
				.equals(inputData.getStatus())) {
			statusLabel = MessageUtils
					.getStringMessageError("inputdata.xls.dao.exception");

		} else if (HubDataInputStatus.ERROR_XLS_VIDE.equals(inputData
				.getStatus())) {
			statusLabel = MessageUtils
					.getStringMessageError("inputdata.xls.empty");

		} else if (HubDataInputStatus.XLS_VALID.equals(inputData.getStatus())) {
			statusLabel = MessageUtils.getStringMessage("inputdata.xls.valid");

		} else if (HubDataInputStatus.FILE_VALID.equals(inputData.getStatus())) {
			statusLabel = MessageUtils.getStringMessage("inputdata.file.valid");

		} else if (HubDataInputStatus.CLOSE.equals(inputData.getStatus())) {
			statusLabel = MessageUtils.getStringMessage("inputdata.close");

		} else {
			throw new ApplicationException(
					"Hub : Impossible de récupérer le libellé"
							+ " du statut pour la ligne de chargement.");
		}
		inputData.setStatusLabel(statusLabel);
	}

	/**
	 * Suppression d'une ligne de chargement. On regarde dans le répertoire de
	 * stockage si le fichier existe, si oui, on le supprime, et on supprime la
	 * ligne dans la table hub_input_data.
	 */
	@Override
	public void delete(HubInputData inputData) throws ApplicationException {

		try {
			String filePath = repositoryFolder + inputData.getFileStoreName();
			// Supprime si le fichier existe.
			if (inputData.getFileStoreName() != null
					&& inputData.getFileStoreName().trim().length() > 0) {
				File fileTodelete = new File(filePath);
				if (fileTodelete.exists())
					fileTodelete.delete();
			}
			// Suppression de la ligne dans la table.
			daoInputData.delete(inputData);
		} catch (DataAccessException e) {
			throw new DaoApplicationException(e);
		} catch (Exception e) {
			throw new ApplicationException(e);
		}
	}

	/**
	 * Ecriture d'un fichier dans le répertoire de stockage et insertion d'une
	 * ligne de traitement dans la table hub_input_data.
	 */
	@Override
	public void uploadFile(FileUploadEvent event, HubTypeFile typeFile)
			throws ApplicationException {

		FileOutputStream fos = null;
		// Création d'un objet de stockage pour le traitement.
		HubInputData inputData = new HubInputData();

		try {
			// Récupération du fichier en upload.
			UploadedFile item = event.getUploadedFile();
			// On récupère le chemin complet du fichier.
			String fileName = item.getName();
			// On ne prend que le nom du fichier.
			fileName = fileName.substring(fileName.lastIndexOf("\\") + 1,
					fileName.length());

			// On vérifie le suffixe du fichier chargé.
			if (!fileName.endsWith(IXlsDecoder.EXCEL_SUFFIX))
				throw new FileNotExcelFormatException(
						"Hub : Le fichier n'est pas au format Excel.");

			String filePath = repositoryFolder + fileName;
			// On met le nom initial du fichier dans l'objet de stockage.
			inputData.setFileAttachedName(fileName);
			// Ouverture d'un objet File pour vérifier si le fichier existe.
			File file = new File(filePath);
			// Si existe alors on sait que le nom est correct car le fichier a
			// déjà été créé.
			if (file.exists()) {
				file.delete();
				// Sinon il faut formater le nom du fichier.
			} else {
				fileName = xlsDecoder.getStoreFileNameXls(typeFile);
			}
			// Ecriture du fichier.
			fos = new FileOutputStream(repositoryFolder + fileName);
			fos.write(item.getData());

			// Ecriture d'une ligne dans la table hub_input_data.
			inputData.setStatus(HubDataInputStatus.FILE_VALID);
			inputData.setFileStoreName(fileName);
			inputData.setDateProcessing(new Date());

		} catch (FileNotFoundException e) {
			inputData.setStatus(HubDataInputStatus.ERROR_XLS_WRITING);
			throw new ApplicationException(e);

		} catch (FileNotExcelFormatException e) {
			inputData.setStatus(HubDataInputStatus.ERROR_XLS_BAD_FORMAT);

		} catch (Exception e) {
			throw new ApplicationException(e);

		} finally {
			try {
				// Insertion de la ligne de traitement dans la table
				// hub_input_data.
				daoInputData.insert(inputData);
				fos.close();

				// On lance directement le processus d'intégration.
				xlsDecoder.decodeXls();

			} catch (DataAccessException e) {
				throw new DaoApplicationException(e);

			} catch (IOException e) {
				throw new ApplicationException(e);
			}
		}
	}

	/**
	 * Méthode pour le chargement manuel de sillons à partir de l'ihm (et non à
	 * partir de fichier xls). On modifie la liste pour la rendre compatible
	 * avec celle initialement générée par la lecture d'un fichier Excel et on
	 * rejoint le tronc commun dans le DAO pour l'intégration des sillons à
	 * l'aide du HubManager (c'est le hubManager qui s'occupe de la conversion
	 * pour la gestion des erreurs).
	 */
	@Override
	public void integrateSillons(List<Sillon> lstSillons)
			throws ApplicationException {

		for (Sillon sillon : lstSillons) {
			// Gestion du client (on vide le champ commentaire qui a servi à
			// véhiculer l'information temporairement)
			if (sillon.getComment().contains(IXlsDecoder.ECR_CUSTOMER)) {
				sillon.setEcrOwner(true);
			}
			sillon.setComment(null);
			// On copie les dates et heures théoriques dans les champ pour
			// les dates et heures réelles.
			sillon.setRealMovementDate(sillon.getMovementDate());
			sillon.setRealMovementTime(sillon.getMovementTime());
			// Traitement départ / arrivée (par défaut la valeur est stockée
			// dans Origin).
			if (HubSillonType.ARRIVAL.getValue().equals(sillon.getType())) {
				sillon.setDestination(IXlsDecoder.HUB_STATION);
			} else {
				sillon.setDestination(sillon.getOrigin());
				sillon.setOrigin(IXlsDecoder.HUB_STATION);
			}
			// On passe les données en majuscules.
			sillon.setOrigin(sillon.getOrigin().toUpperCase());
			sillon.setDestination(sillon.getDestination().toUpperCase());
			sillon.setTcmsTrainId(sillon.getTcmsTrainId().toUpperCase());
			// On positionne le statut.
			sillon.setStatusId(HubSillonStatus.ATTRIBUTED.getValue());
		}
		// Intégration des sillons dans la base de données.
		hubManager.integrateSillons(lstSillons);
	}

	/**
	 * Méthode pour le chargement manuel de compositions à partir de l'ihm (et
	 * non à partir de fichier xls). On modifie la liste pour la rendre
	 * compatible avec celle initialement générée par la lecture d'un fichier
	 * Excel et on rejoint le tronc commun dans le DAO pour l'intégration des
	 * compositions à l'aide du HubManager (c'est le hubManager qui s'occupe de
	 * la conversion pour la gestion des erreurs).
	 * 
	 * IMPORTANT : Pour obtenir une réinitialisation propre des données, on a
	 * mappé la liste de selection des destinations sur le champ commentaire,
	 * cette liste est juste utilisée pour pouvoir envoyer le code destination
	 * dans le champ destinationExtId qui lui est inséré dans la base de
	 * données. Comme le champ commentaire de la composition n'est pas inséré
	 * dans la base, il n'y a pas besoin d'effacer la valeur temporaire qui a
	 * été mise, on le laisse donc tel quel.
	 */
	@Override
	public void integrateCompositions(List<Composition> lstCompositions,
			String tcmsTrainId) throws ApplicationException {

		// Initialisation.
		int numLot = 0;
		int railId = 0;
		int posNum = 0;
		int prevRailId = 0;

		tcmsTrainId = tcmsTrainId.toUpperCase();
		// On met en conformité la liste des compositions par rapport au
		// décodeur Xls.
		for (Composition composition : lstCompositions) {

			// Insere la position du wagon dans la composition.
			composition.getWagon().getCurrentPos().setPosNum(posNum);

			try {
				// Récupération de la voie pour la destination.
				DestinationRailTranscoder transcoder = destinationRailTranscoder
						.getRailForDestination(composition
								.getDestinationExtId());
				composition.getWagon().getCurrentPos()
						.setPosRailId(transcoder.getRailId());

				// Ceci est la destination finale du wagon qui va le suivre dans
				// tout le hub, même si le wagon change de voie.
				composition.getWagon().setDestinationId(
						transcoder.getDestinationId());

				// On copie la voie dans le champ de sauvegarde.
				composition.setPosRailSaveId(transcoder.getRailId());

			} catch (DestinationUnknownException e) {
				String errMsg = MessageUtils
						.getStringMessageError("inputdata.ihm.bad.destination");
				composition.setErrorMessage(errMsg);
				throw e;
			}

			// Calcul du lot. On fait ce calcul en fonction de la voie et non
			// de l'id de destination car plusieurs destinations peuvent aller
			// sur la même voie.
			railId = composition.getWagon().getCurrentPos().getPosRailId();
			if (prevRailId != railId) {
				prevRailId = railId;
				numLot++;
			}

			// Insertion du numéro de lot.
			composition.getWagon().setNumLot(numLot);

			posNum++;
		}
		// On rejoint le tronc commun.
		hubManager.integrateComposition(lstCompositions, tcmsTrainId, null);
	}
}
